/********************************************************************************
* @file    os_api.c
* @author  jianqiang.xue
* @Version V1.0.0
* @Date    2021-04-03
* @brief   
********************************************************************************/

#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "cmsis_os2.h"
#include "os_api.h"

#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"

#define IS_IRQ_MASKED()           (__get_PRIMASK() != 0U)
#define IS_IRQ_MODE()             (__get_IPSR() != 0U)
#define IS_IRQ()                  (IS_IRQ_MODE() || (IS_IRQ_MASKED() && (KernelState == osKernelRunning)))
#define MAX_BITS_TASK_NOTIFY      31U
#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY)  - 1U))
/* Kernel initialization state */
static osKernelState_t KernelState = osKernelInactive;
/************************************OS_KERNEL************************************/
os_status os_kernel_initialize(void)
{
    return (os_status)osKernelInitialize();
}

os_status os_kernel_start(void)
{
    return (os_status)osKernelStart();
}

os_status os_kernel_lock(void)
{
    return (os_status)osKernelLock();
}

os_status os_kernel_unlock(void)
{
    return (os_status)osKernelUnlock();
}

os_status os_delay(uint32_t ms)
{
    return (os_status)osDelay(ms);
}

uint32_t os_get_tick(void)
{
    return osKernelGetTickCount();
}

/************************************OS_THREAD************************************/
os_thread_id os_thread_create(const os_thread_def_t *thread_def, void *arg)
{
    if (thread_def == NULL)
    {
        return NULL;
    }
    osThreadAttr_t attr = {0};
    attr.name = arg;
    attr.priority = osPriorityLow;
    if (thread_def->tpriority == OS_PRIORITY_LOW)
    {
        attr.priority = osPriorityLow;
    }
    else if (thread_def->tpriority == OS_PRIORITY_NORMAL)
    {
        attr.priority = osPriorityNormal;
    }
    else if (thread_def->tpriority == OS_PRIORITY_ABOVENORMAL)
    {
        attr.priority = osPriorityAboveNormal;
    }
    else if (thread_def->tpriority == OS_PRIORITY_HIGH)
    {
        attr.priority = osPriorityHigh;
    }
    else if (thread_def->tpriority == OS_PRIORITY_REALTIME)
    {
        attr.priority = osPriorityRealtime;
    }
    else
    {
        attr.priority = osPriorityLow;
    }
    attr.stack_size = thread_def->stacksize;
    attr.attr_bits = osThreadDetached;
    return (os_thread_id)osThreadNew((osThreadFunc_t)thread_def->pthread, arg, &attr);
}

/************************************OS_TIMER************************************/
os_timer_id os_timer_create(const os_timer_def_t *timer_def, os_timer_t type, void *arg)
{
    return osTimerNew((osTimerFunc_t)timer_def->ptimer, (osTimerType_t)type, arg, NULL);
}

os_status os_timer_start(os_timer_id timer_id, uint32_t millisec)
{
    return (os_status)osTimerStart(timer_id, millisec);
}

os_status os_timer_stop(os_timer_id timer_id)
{
    return (os_status)osTimerStop(timer_id);
}

/************************************OS_MAIL************************************/
os_mail_qid os_mail_create(const os_mailq_def_t *queue_def, os_thread_id thread_id)
{
    return (os_mail_qid)osMessageQueueNew(queue_def->queue_sz, queue_def->item_sz, NULL);
}

void *os_mail_alloc(os_mail_qid queue_id, uint32_t millisec)
{
    return NULL;
}

void *os_mail_clean_and_alloc(os_mail_qid queue_id, uint32_t millisec)
{
    return os_mail_alloc(queue_id, millisec);
}

os_status os_mail_put(os_mail_qid queue_id, void *mail)
{
    return (os_status)osMessageQueuePut((osMessageQueueId_t)queue_id, mail, NULL, NULL);
}

os_event os_mail_get(os_mail_qid queue_id, uint32_t millisec, void *arg)
{
    osStatus_t status;
    os_event event_t;
    status = osMessageQueueGet((osMessageQueueId_t)queue_id, arg, NULL, millisec);
    event_t.status = (os_status)status;
    event_t.def.message_id = (os_message_qid)queue_id;
    event_t.value.p = arg;
    return event_t;
}

os_status os_mail_free(os_mail_qid queue_id, void *mail)
{
    return (os_status)0;
}

/************************************OS_POOL************************************/
os_pool_id os_pool_create(const os_pool_def_t *pool_def)
{
    return (os_pool_id)osMemoryPoolNew(pool_def->pool_sz, pool_def->item_sz, NULL);
}

void *os_pool_alloc(os_pool_id pool_id)
{
    return osMemoryPoolAlloc((osMemoryPoolId_t)pool_id, 0);
}

void *os_pool_calloc(os_pool_id pool_id)
{
   return os_pool_alloc(pool_id);
}

os_status os_pool_free(os_pool_id pool_id, void *block)
{
    return (os_status)osMemoryPoolFree((osMemoryPoolId_t)pool_id, block);
}
/************************************OS_MSG_QUEUE************************************/
os_message_qid os_message_create(const os_messageq_def_t *queue_def, os_thread_id thread_id)
{
    return (os_message_qid)osMessageQueueNew(queue_def->queue_sz, 4, &(queue_def->attr));
}

os_status os_message_put(os_message_qid queue_id, uint32_t info, uint32_t millisec)
{
    return (os_status)osMessageQueuePut((osMessageQueueId_t)queue_id, (const void *)&info, 0, millisec); // Send Message
}

os_event os_message_get(os_message_qid queue_id, uint32_t millisec)
{
    QueueHandle_t hQueue = (QueueHandle_t)queue_id;
    BaseType_t yield;
    uint32_t *msg_ptr;
    os_event event;

    event.status = OS_OK;
    if (IS_IRQ())
    {
        if ((hQueue == NULL) || (millisec != 0U))
        {
            event.status = OS_ERROR_PARAMETER;
        }
        else
        {
            yield = false;
            if (xQueueReceiveFromISR(hQueue, &msg_ptr, &yield) != true)
            {
                event.status = OS_ERROR_RESOURCE;
            }
            else
            {
                event.status = OS_EVENT_MESSAGE;
                event.value.p = (void *)msg_ptr;
                portYIELD_FROM_ISR(yield);
            }
        }
    }
    else
    {
        if (hQueue == NULL)
        {
            event.status = OS_ERROR_PARAMETER;
        }
        else
        {
            if (xQueueReceive(hQueue, &msg_ptr, (TickType_t)millisec) != true)
            {
                if (millisec != 0U)
                {
                    event.status = OS_EVENT_TIMEOUT;
                }
                else
                {
                    event.status = OS_ERROR_RESOURCE;
                }
            }
            else
            {
                event.status = OS_EVENT_MESSAGE;
                event.value.p = (void *)msg_ptr;
                //LOG_D("os_msg_get:0x%x|0x%x", queue_id, msg_ptr);
            }
        }
    }

    return event;
}

uint8_t os_message_get_space(os_message_qid queue_id)
{
    return (uint8_t)osMessageQueueGetSpace(queue_id);
}

uint8_t os_message_get_count(os_message_qid queue_id)
{
    return (uint8_t)osMessageQueueGetCount(queue_id);
}

/************************************OS_SIGNAL************************************/
int32_t isr_signal_set(os_thread_id thread_id, int32_t signals)
{
    return osThreadFlagsSet((osThreadId_t)thread_id, signals);
}

int32_t os_signal_set(os_thread_id thread_id, int32_t signals)
{
    return osThreadFlagsSet((osThreadId_t)thread_id, signals);
}

int32_t os_signal_clear(os_thread_id thread_id, int32_t signals)
{
    return osThreadFlagsClear(signals);
}

// signals = 0,则等待任意信号.
os_event os_signal_wait(int32_t signals, uint32_t millisec)
{
    BaseType_t rval;
    os_event event_t;

    if (IS_IRQ())
    {
        event_t.status = OS_ERROR_ISR;
    }
    else if ((signals & THREAD_FLAGS_INVALID_BITS) != 0U)
    {
        event_t.status = OS_ERROR_PARAMETER;
    }
    else
    {
        rval = xTaskNotifyWait(signals, 0xFFFFFFFF, (uint32_t *)&(event_t.value.signals), millisec);
        if (rval == true)
        {
            event_t.status = OS_EVENT_SIGNAL;
        }
        else
        {
            event_t.status = OS_EVENT_TIMEOUT;
        }
    }

    /* Return flags before clearing */
    return (event_t);
}
